home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir37 / ushell.zip / SHELL / SH7.C < prev    next >
C/C++ Source or Header  |  1990-02-19  |  33KB  |  1,828 lines

  1. /* MS-DOS SHELL - Internal Command Processing
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  *    $Header: sh7.c 1.1 90/01/29 17:46:25 MS_user Exp $
  17.  *
  18.  *    $Log:    sh7.c $
  19.  * Revision 1.1  90/01/29  17:46:25  MS_user
  20.  * Initial revision
  21.  * 
  22.  * 
  23.  */
  24.  
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <stdio.h>
  28. #include <process.h>
  29. #include <dos.h>
  30. #include <signal.h>
  31. #include <errno.h>
  32. #include <setjmp.h>
  33. #include <ctype.h>
  34. #include <string.h>
  35. #include <unistd.h>
  36. #include <stdlib.h>
  37. #include <fcntl.h>
  38. #include <limits.h>
  39. #include <stdarg.h>
  40. #include "sh.h"
  41.  
  42. #define    SECS        60L
  43. #define    MINS        3600L
  44. #define IS_OCTAL(a)    (((a) >= '0') && ((a) <= '7'))
  45.  
  46. /* Definitions for test */
  47.  
  48. #define END_OF_INPUT    0
  49. #define FILE_READABLE    1
  50. #define FILE_WRITABLE    2
  51. #define FILE_REGULAR    3
  52. #define FILE_DIRECTORY    4
  53. #define FILE_NONZERO    5
  54. #define FILE_TERMINAL    6
  55. #define STRING_ZERO    7
  56. #define STRING_NONZERO    8
  57. #define STRING_EQUAL    9
  58. #define STRING_NOTEQUAL    10
  59. #define NUMBER_EQUAL    11
  60. #define NUMBER_NOTEQUAL    12
  61. #define NUMBER_EQ_GREAT    13
  62. #define NUMBER_GREATER    14
  63. #define NUMBER_EQ_LESS    15
  64. #define NUMBER_LESS    16
  65. #define UNARY_NOT    17
  66. #define BINARY_AND    18
  67. #define BINARY_OR    19
  68. #define LPAREN        20
  69. #define RPAREN        21
  70. #define OPERAND        22
  71. #define FILE_EXECUTABLE    23
  72. #define FILE_USER    24
  73. #define FILE_GROUP    25
  74. #define FILE_TEXT    26
  75. #define FILE_BLOCK    27
  76. #define FILE_CHARACTER    28
  77. #define FILE_FIFO    29
  78.  
  79. #define UNARY_OP    1
  80. #define BINARY_OP    2
  81. #define B_UNARY_OP    3
  82. #define B_BINARY_OP    4
  83. #define PAREN        5
  84.  
  85. static struct test_op {
  86.     char    *op_text;
  87.     short     op_num;
  88.     short     op_type;
  89. } test_ops[] = {
  90.     {"-r",    FILE_READABLE,        UNARY_OP},
  91.     {"-w",    FILE_WRITABLE,        UNARY_OP},
  92.     {"-x",    FILE_EXECUTABLE,    UNARY_OP},
  93.     {"-f",    FILE_REGULAR,        UNARY_OP},
  94.     {"-d",    FILE_DIRECTORY,        UNARY_OP},
  95.     {"-s",    FILE_NONZERO,        UNARY_OP},
  96.     {"-t",    FILE_TERMINAL,        UNARY_OP},
  97.     {"-z",    STRING_ZERO,        UNARY_OP},
  98.     {"-n",    STRING_NONZERO,        UNARY_OP},
  99.     {"=",    STRING_EQUAL,        BINARY_OP},
  100.     {"!=",    STRING_NOTEQUAL,    BINARY_OP},
  101.     {"-eq",    NUMBER_EQUAL,        BINARY_OP},
  102.     {"-ne",    NUMBER_NOTEQUAL,    BINARY_OP},
  103.     {"-ge",    NUMBER_EQ_GREAT,    BINARY_OP},
  104.     {"-gt",    NUMBER_GREATER,        BINARY_OP},
  105.     {"-le",    NUMBER_EQ_LESS,        BINARY_OP},
  106.     {"-lt",    NUMBER_LESS,        BINARY_OP},
  107.     {"!",    UNARY_NOT,        B_UNARY_OP},
  108.     {"-a",    BINARY_AND,        B_BINARY_OP},
  109.     {"-o",    BINARY_OR,        B_BINARY_OP},
  110.     {"(",    LPAREN,            PAREN},
  111.     {")",    RPAREN,            PAREN},
  112. #ifdef S_IFCHR
  113.     {"-c",    FILE_CHARACTER,        UNARY_OP},
  114. #endif
  115. #ifdef S_IFBLK
  116.     {"-b",    FILE_BLOCK,        UNARY_OP},
  117. #endif
  118. #ifdef S_ISUID
  119.     {"-u",    FILE_USER,        UNARY_OP},
  120. #endif
  121. #ifdef S_ISGID
  122.     {"-g",    FILE_GROUP,        UNARY_OP},
  123. #endif
  124. #ifdef S_ISVTX
  125.     {"-k",    FILE_TEXT,        UNARY_OP},
  126. #endif
  127. #ifdef S_IFIFO
  128.     {"-p",    FILE_FIFO,        UNARY_OP},
  129. #endif
  130.     {(char *)NULL,    NULL,        NULL}
  131. };
  132.  
  133. static int        expr (int);
  134. static int        bexpr (int);
  135. static int        primary (int);
  136. static int        lex (char *);
  137. static long        num (char *);
  138. static void        syntax (void);
  139. static int        dolabel (C_Op *);
  140. static int        dochdir (C_Op *);
  141. static int        dodrive (C_Op *);
  142. static int        doshift (C_Op *);
  143. static int        doumask (C_Op *);
  144. static int        dodot (C_Op *);
  145. static int        doecho (C_Op *);
  146. static int        dogetopt (C_Op *);
  147. static int        dopwd (C_Op *);
  148. static int        doswap (C_Op *);
  149. static int        dounset (C_Op *);
  150. static int        dotype (C_Op *);
  151. static int        dotest (C_Op *);
  152. static int        dover (C_Op *);
  153. static int        doread (C_Op *);
  154. static int        doeval (C_Op *);
  155. static int        dotrap (C_Op *);
  156. static int        getsig (char *);
  157. static int        dobreak (C_Op *);
  158. static int        docontinue (C_Op *);
  159. static int        brkcontin (char *, int);
  160. static int        doexit (C_Op *);
  161. static int        doexec (C_Op *);
  162. static int        doreturn (C_Op *);
  163. static int        doexport (C_Op *);
  164. static int        domsdos (C_Op *);
  165. static int        doreadonly (C_Op *);
  166. static int        doset (C_Op *);
  167. static int        dohistory (C_Op *);
  168. static void        setsig (int, int (*)());
  169. static int        rdexp (char **, int, char *);
  170. static void        v1_putsn (char *, int);
  171.  
  172. static char        **test_alist;
  173. static struct test_op    *test_op;
  174. static jmp_buf        test_jmp;
  175.  
  176. /*
  177.  * built-in commands: doX
  178.  */
  179.  
  180. static int    dolabel (t)
  181. C_Op        *t;
  182. {
  183.     return 0;
  184. }
  185.  
  186. /*
  187.  * Getopt - split arguments.  getopts pattern args
  188.  */
  189.  
  190. static int    dogetopt (t)
  191. register C_Op    *t;
  192. {
  193.     int            argc;
  194.     char        **argv = t->words;
  195.     int            c;
  196.  
  197. /* Count arguments */
  198.  
  199.     optind = 1;                /* Reset the optind flag    */
  200.     opterr = 1;                /* Reset the error flag        */
  201.  
  202.     for (argc = 0; t->words[argc] != (char *)NULL; argc++);
  203.  
  204.     if (argc < 2)
  205.     {
  206.     S_puts ("usage: getopt legal-args $*\n");
  207.     return 2;
  208.     }
  209.  
  210.     argc -= 2;
  211.     argv += 2;
  212.  
  213. /* Scan each argument */
  214.  
  215.     while ((c = getopt (argc, argv, t->words[1])) != EOF)
  216.     {
  217.     if (c == '?')
  218.         return 2;
  219.  
  220.     v1printf ("-%c ", c);
  221.  
  222. /* Check for addition parameter */
  223.  
  224.     if (*(strchr (t->words[1], c) + 1) == ':')
  225.     {
  226.         v1_puts (optarg);
  227.         v1_putc (SP);
  228.     }
  229.     }
  230.  
  231.     v1_puts ("-- ");
  232.     argv += optind;
  233.  
  234.     while (optind++ < argc)
  235.     {
  236.     v1_puts (*argv++);
  237.     v1_putc ((char)((optind == argc) ? NL : SP));
  238.     }
  239.  
  240.     return 0;
  241. }
  242.  
  243. /*
  244.  * Echo the parameters
  245.  */
  246.  
  247. static int    doecho (t)
  248. register C_Op    *t;
  249. {
  250.     int        n = 1;
  251.     int        no_eol = 0;        /* No EOL            */
  252.     char    *ip;            /* Input pointer        */
  253.     int        c_val;            /* Current character        */
  254.     char    c;
  255.     bool    end_s;
  256.     char    *cp = e.linep;
  257.                     /* Always leave room for NL    */
  258.     char    *ep = &e.linep[LINE_MAX - 3];
  259.  
  260.     while ((ip = t->words[n++]) != (char *)NULL)
  261.     {
  262.     if ((n == 2) && (strcmp (ip, "-n") == 0))
  263.     {
  264.         no_eol++;
  265.         continue;
  266.     }
  267.  
  268. /* Process the string */
  269.  
  270.     end_s = FALSE;
  271.  
  272.     do
  273.     {
  274.  
  275. /* Any special character processing ? */
  276.  
  277.         if ((c = *(ip++)) == '\\')
  278.         {
  279.         if ((c_val = Process_Escape (&ip)) == -1)
  280.         {
  281.             no_eol = 1;
  282.             continue;
  283.         }
  284.  
  285.         c = (char)c_val;
  286.         }
  287.  
  288. /* End of string - check to see if a space if required */
  289.  
  290.         else if (c == 0)
  291.         {
  292.         end_s = TRUE;
  293.  
  294.         if (t->words[n] != (char *)NULL)
  295.             c = SP;
  296.  
  297.         else
  298.             continue;
  299.         }
  300.  
  301. /* Output the character */
  302.  
  303.         if (cp < ep)
  304.         *(cp++) = c;
  305.  
  306.         else
  307.         {
  308.         v1_putsn (e.linep, (int)(cp - e.linep));
  309.         cp = e.linep;
  310.         }
  311.  
  312.     } while (!end_s);
  313.     }
  314.  
  315. /* Is EOL required ? */
  316.  
  317.     if (!no_eol)
  318.     *(cp++) = NL;
  319.  
  320. /* Flush buffer */
  321.  
  322.     if ((n = (int)(cp - e.linep)))
  323.     v1_putsn (e.linep, n);
  324.  
  325.     return 0;
  326. }
  327.  
  328. /*
  329.  * Process_Escape - Convert an escaped character to a binary value.
  330.  *
  331.  * Returns the binary value and updates the string pointer.
  332.  */
  333.  
  334. int    Process_Escape (cp)
  335. char    **cp;                    /* Pointer to character */
  336. {
  337.     int        c_val = **cp;            /* Current character    */
  338.  
  339.     if (c_val)
  340.         (*cp)++;
  341.  
  342. /* Process escaped characters */
  343.  
  344.     switch (c_val)
  345.     {
  346.         case 'b':            /* Backspace                    */
  347.             return 0x08;
  348.  
  349.         case 'f':            /* Form Feed                    */
  350.             return 0x0c;
  351.  
  352.         case 'v':            /* Vertical Tab                 */
  353.             return 0x0b;
  354.  
  355.         case 'n':            /* New Line                     */
  356.             return 0x0a;
  357.  
  358.         case 'r':            /* Carriage return              */
  359.             return 0x0d;
  360.  
  361.         case 't':            /* Forward tab                  */
  362.         return 0x09;
  363.  
  364.         case '\\':            /* Backslash                    */
  365.         return '\\';
  366.  
  367.         case 'c':            /* no eol            */
  368.         return -1;
  369.     }
  370.  
  371. /* Check for an octal string */
  372.  
  373.     if ((c_val >= 0) && (c_val < 8))
  374.     {
  375.     while ((IS_OCTAL (**cp)))
  376.         c_val = (c_val * 8) + *((*cp)++) - '0';
  377.  
  378.     return c_val;
  379.     }
  380.  
  381.     return c_val;
  382. }
  383.  
  384. /*
  385.  * Display the current version
  386.  */
  387.  
  388. static int    dover (t)
  389. C_Op        *t;
  390. {
  391.     v1printf (Copy_Right1, _osmajor, _osminor);
  392.     v1a_puts (Copy_Right2);
  393.     return 0;
  394. }
  395.  
  396. static char    *swap_device[] = {"disk", "extend", "expand"};
  397.  
  398. /*
  399.  * Modify swapping information: swap options
  400.  */
  401.  
  402. static int    doswap (t)
  403. register C_Op    *t;
  404. {
  405.     register int    n = 1;
  406.     char        *cp;
  407.  
  408. /* Display current values ? */
  409.  
  410.     if (t->words[1] == (char *)NULL)
  411.     {
  412.     if (Swap_Mode == SWAP_OFF)
  413.         v1a_puts ("Swapping disabled");
  414.  
  415.     else
  416.     {
  417.         register int    j;
  418.  
  419.         v1_puts ("Swap devices: ");
  420.  
  421.         for (j = 0, n = 1; j < 3; ++j, n <<= 1)
  422.         {
  423.         if (Swap_Mode & n)
  424.         {
  425.             v1printf ("%s ", swap_device[j]);
  426.  
  427.             if (n == SWAP_EXTEND)
  428.             v1printf ("(0x%.6lx) ", SW_EMstart);
  429.         }
  430.         }
  431.  
  432.         v1_putc (NL);
  433.     }
  434.  
  435.     return 0;
  436.     }
  437.  
  438. /* Set up new values */
  439.  
  440.     Swap_Mode = SWAP_OFF;
  441.  
  442.     while ((cp = t->words[n++]) != (char *)NULL)
  443.     {
  444.     if (strcmp (cp, "off") == 0)
  445.         Swap_Mode = SWAP_OFF;
  446.  
  447.     else if (strcmp (cp, "on") == 0)
  448.         Swap_Mode = SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND;
  449.  
  450. /* Scan for valid arguments */
  451.  
  452.     else
  453.     {
  454.         register int    j, k;
  455.  
  456.         for (j = 0, k = 1; j < 3; ++j, k <<= 1)
  457.         {
  458.         if (strcmp (cp, swap_device[j]) == 0)
  459.         {
  460.             Swap_Mode |= k;
  461.  
  462. /* If extended memory, they can specify the start address as a hex number */
  463.  
  464.             if (k == SWAP_EXTEND)
  465.             {
  466.             char    *sp;
  467.             long    start;
  468.  
  469. /* Check for not changed */
  470.  
  471.             if ((sp = t->words[n]) == (char *)NULL)
  472.                 break;
  473.  
  474. /* Convert hex number */
  475.  
  476.             start = strtol (sp, &sp, 16);
  477.  
  478. /* If not completely a hex number, ignore */
  479.  
  480.             if (*sp)
  481.                 break;
  482.  
  483. /* Set used and saved new value */
  484.  
  485.             SW_EMstart = start;
  486.             ++n;
  487.  
  488.             if ((SW_EMstart < 0x100000L) ||
  489.                 (SW_EMstart > 0xf00000L))
  490.                 SW_EMstart = 0x100000L;
  491.  
  492.             v1printf ("Extend memory start set to 0x%.6lx\n",
  493.                   SW_EMstart);
  494.             }
  495.  
  496.             break;
  497.         }
  498.         }
  499.     }
  500.     }
  501.  
  502.     return 0;
  503. }
  504.  
  505. /*
  506.  * Output the current path: pwd
  507.  */
  508.  
  509. static int    dopwd (t)
  510. register C_Op    *t;
  511. {
  512.     v1a_puts (C_dir->value);
  513.     return 0;
  514. }
  515.  
  516. /*
  517.  * Unset a variable: unset <flag..> <variable name...>
  518.  */
  519.  
  520. static int    dounset (t)
  521. register C_Op    *t;
  522. {
  523.     register int    n = 1;
  524.  
  525.     while (t->words[n] != (char *)NULL)
  526.         unset (t->words[n++], FALSE);
  527.  
  528.     return 0;
  529. }
  530.  
  531. /* Delete a variable or function.  If all is set, system variables can be
  532.  * deleted.  This is used to delete the trap functions
  533.  */
  534.  
  535. void        unset (cp, all)
  536. register char    *cp;
  537. bool        all;
  538. {
  539.     register Var_List        *vp;
  540.     register Var_List        *pvp;
  541.  
  542. /* Unset a flag */
  543.  
  544.     if (*cp == '-')
  545.     {
  546.     while (*(++cp) != 0)
  547.     {
  548.         if (islower (*cp))
  549.         FL_CLEAR (*cp);
  550.     }
  551.  
  552.     setdash ();
  553.     return;
  554.     }
  555.  
  556. /* Ok - unset a variable and not a local value */
  557.  
  558.     if (!all && !(isalpha (*cp)))
  559.     return;
  560.  
  561. /* Check in list */
  562.  
  563.     pvp = (Var_List *)NULL;
  564.  
  565.     for (vp = vlist; (vp != (Var_List *)NULL) && !eqname (vp->name, cp);
  566.      vp = vp->next)
  567.     pvp = vp;
  568.  
  569. /* If not found, delete the function if it exists */
  570.  
  571.     if (vp == (Var_List *)NULL)
  572.     {
  573.     Fun_Ops     *fp;
  574.  
  575.     if ((fp = Fun_Search (cp)) != (Fun_Ops *)NULL)
  576.         Save_Function (fp->tree, TRUE);
  577.  
  578.     return;
  579.     }
  580.  
  581. /* Error if read-only */
  582.  
  583.     if (vp->status & (RONLY | PONLY))
  584.     {
  585.     if ((cp = strchr (vp->name, '=')) != (char *)NULL)
  586.         *cp = 0;
  587.  
  588.     S_puts (vp->name);
  589.  
  590.     if (cp != (char *)NULL)
  591.         *cp = '=';
  592.  
  593.     S_puts ((vp->status & PONLY) ? ": cannot unset\n" : " is read-only\n");
  594.     return;
  595.     }
  596.  
  597. /* Delete it */
  598.  
  599.     if (vp->status & GETCELL)
  600.     DELETE (vp->name);
  601.  
  602.     if (pvp == (Var_List *)NULL)
  603.     vlist = vp->next;
  604.  
  605.     else
  606.     pvp->next = vp->next;
  607.  
  608.     DELETE (vp);
  609. }
  610.  
  611. /*
  612.  * Execute a test: test <arguments>
  613.  */
  614.  
  615. static int    dotest (t)
  616. register C_Op    *t;
  617. {
  618.     int        st = 0;
  619.  
  620.     if (*(test_alist = &t->words[1]) == (char *)NULL)
  621.     return 1;
  622.  
  623. /* If [ <arguments> ] form, check for end ] and remove it */
  624.  
  625.     if (strcmp (t->words[0], "[") == 0)
  626.     {
  627.     while (t->words[++st] != (char *)NULL)
  628.         ;
  629.  
  630.     if (strcmp (t->words[--st], "]") != 0)
  631.     {
  632.         print_error ("test: missing ']'\n");
  633.         return 1;
  634.     }
  635.  
  636.     else
  637.         t->words[st] = (char *)NULL;
  638.     }
  639.  
  640. /* Set abort address */
  641.  
  642.     if (setjmp (test_jmp))
  643.     return 1;
  644.  
  645.     st = !expr (lex (*test_alist));
  646.  
  647.     if (*(++test_alist) != (char *)NULL)
  648.     syntax ();
  649.  
  650.     return (st);
  651. }
  652.  
  653. static int    expr (n)
  654. int        n;
  655. {
  656.     int        res;
  657.  
  658.     if (n == END_OF_INPUT)
  659.     syntax ();
  660.  
  661.     res = bexpr (n);
  662.  
  663.     if (lex (*(++test_alist)) == BINARY_OR)
  664.     return expr (lex (*(++test_alist))) || res;
  665.  
  666.     test_alist--;
  667.     return res;
  668. }
  669.  
  670. static int    bexpr (n)
  671. int        n;
  672. {
  673.     int res;
  674.  
  675.     if (n == END_OF_INPUT)
  676.     syntax ();
  677.  
  678.     res = primary (n);
  679.     if (lex (*(++test_alist)) == BINARY_AND)
  680.     return bexpr (lex (*(++test_alist))) && res;
  681.  
  682.     test_alist--;
  683.     return res;
  684. }
  685.  
  686. static int    primary (n)
  687. int        n;
  688. {
  689.     register char    *opnd1, *opnd2;
  690.     struct stat        s;
  691.     int            res;
  692.  
  693.     if (n == END_OF_INPUT)
  694.     syntax ();
  695.  
  696.     if (n == UNARY_NOT)
  697.     return !expr (lex (*(++test_alist)));
  698.  
  699.     if (n == LPAREN)
  700.     {
  701.     res = expr (lex (*(++test_alist)));
  702.  
  703.     if (lex (*(++test_alist)) != RPAREN)
  704.         syntax ();
  705.  
  706.     return res;
  707.     }
  708.  
  709.     if (n == OPERAND)
  710.     {
  711.     opnd1 = *test_alist;
  712.     (void) lex (*(++test_alist));
  713.  
  714.     if ((test_op != (C_Op *)NULL) && test_op->op_type == BINARY_OP)
  715.     {
  716.         struct test_op *op = test_op;
  717.  
  718.         if ((opnd2 = *(++test_alist)) == (char *)NULL)
  719.         syntax ();
  720.  
  721.         switch (op->op_num)
  722.         {
  723.         case STRING_EQUAL:
  724.             return strcmp (opnd1, opnd2) == 0;
  725.  
  726.         case STRING_NOTEQUAL:
  727.             return strcmp (opnd1, opnd2) != 0;
  728.  
  729.         case NUMBER_EQUAL:
  730.             return num (opnd1) == num (opnd2);
  731.  
  732.         case NUMBER_NOTEQUAL:
  733.             return num (opnd1) != num (opnd2);
  734.  
  735.         case NUMBER_EQ_GREAT:
  736.             return num (opnd1) >= num (opnd2);
  737.  
  738.         case NUMBER_GREATER:
  739.             return num (opnd1) > num (opnd2);
  740.  
  741.         case NUMBER_EQ_LESS:
  742.             return num (opnd1) <= num (opnd2);
  743.  
  744.         case NUMBER_LESS:
  745.             return num (opnd1) < num (opnd2);
  746.         }
  747.     }
  748.  
  749.     test_alist--;
  750.     return strlen (opnd1) > 0;
  751.     }
  752.  
  753. /* unary expression */
  754.  
  755.     if (test_op->op_type != UNARY_OP || *++test_alist == 0)
  756.     syntax ();
  757.  
  758.     switch (n)
  759.     {
  760.     case STRING_ZERO:
  761.         return strlen (*test_alist) == 0;
  762.  
  763.     case STRING_NONZERO:
  764.         return strlen (*test_alist) != 0;
  765.  
  766.     case FILE_READABLE:
  767.         return access (*test_alist, R_OK) == 0;
  768.  
  769.     case FILE_WRITABLE:
  770.         return access (*test_alist, W_OK) == 0;
  771.  
  772.     case FILE_EXECUTABLE:
  773.         return access (*test_alist, X_OK) == 0;
  774.  
  775.     case FILE_REGULAR:
  776.         return stat (*test_alist, &s) == 0 && S_ISREG(s.st_mode);
  777.  
  778.     case FILE_DIRECTORY:
  779.         return stat (*test_alist, &s) == 0 && S_ISDIR(s.st_mode);
  780.  
  781.     case FILE_NONZERO:
  782.         return stat (*test_alist, &s) == 0 && (s.st_size > 0L);
  783.  
  784.     case FILE_TERMINAL:
  785.         return isatty ((int)num (*test_alist));
  786.  
  787. #ifdef S_ISUID
  788.     case FILE_USER:
  789.         return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISUID);
  790. #endif
  791.  
  792. #ifdef S_ISGID
  793.     case FILE_GROUP:
  794.         return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISGID);
  795. #endif
  796.  
  797. #ifdef S_ISVTX
  798.     case FILE_TEXT:
  799.         return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISVTX);
  800. #endif
  801.  
  802. #ifdef S_IFBLK
  803.     case FILE_BLOCK:
  804.         return stat (*test_alist, &s) == 0 && S_ISBLK(s.st_mode);
  805. #endif
  806.  
  807. #ifdef S_IFCHR
  808.     case FILE_CHARACTER:
  809.         return stat (*test_alist, &s) == 0 && S_ISCHR(s.st_mode);
  810. #endif
  811.  
  812. #ifdef S_IFIFO
  813.     case FILE_FIFO:
  814.         return stat (*test_alist, &s) == 0 && S_ISFIFO(s.st_mode);
  815. #endif
  816.     }
  817. }
  818.  
  819. static int    lex (s)
  820. register char    *s;
  821. {
  822.     register struct test_op    *op = test_ops;
  823.  
  824.     if (s == (char *)NULL)
  825.     return END_OF_INPUT;
  826.  
  827.     while (op->op_text)
  828.     {
  829.     if (strcmp (s, op->op_text) == 0)
  830.     {
  831.         test_op = op;
  832.         return op->op_num;
  833.     }
  834.  
  835.     op++;
  836.     }
  837.  
  838.     test_op = (struct test_op *)NULL;
  839.     return OPERAND;
  840. }
  841.  
  842. /*
  843.  * Get a long numeric value
  844.  */
  845.  
  846. static long    num (s)
  847. register char    *s;
  848. {
  849.     char    *ep;
  850.     long    l = strtol (s, &ep, 10);
  851.  
  852.     if (!*s || *ep)
  853.     syntax ();
  854.  
  855.     return l;
  856. }
  857.  
  858. /*
  859.  * test syntax error - abort
  860.  */
  861.  
  862. static void    syntax ()
  863. {
  864.     print_error ("test: syntax error\n");
  865.     longjmp (test_jmp, 1);
  866. }
  867.  
  868. /*
  869.  * Select a new drive: x:
  870.  *
  871.  * Select the drive, get the current directory and check that we have
  872.  * actually selected the drive
  873.  */
  874.  
  875. static int    dodrive (t)
  876. register C_Op    *t;
  877. {
  878.     unsigned int    cdrive;
  879.     unsigned int    ndrive = tolower (**t->words) - 'a' + 1;
  880.  
  881.     _dos_setdrive (ndrive, &cdrive);
  882.     Getcwd ();
  883.     _dos_getdrive (&cdrive);
  884.     return (ndrive == cdrive) ? 0 : 1;
  885. }
  886.  
  887. /*
  888.  * Select a new directory: cd
  889.  */
  890.  
  891. static int    dochdir (t)
  892. register C_Op    *t;
  893. {
  894.     char        *p;
  895.     char        *nd;
  896.     register char    *cp;
  897.     int            first = 0;
  898.     unsigned int    dummy;
  899.     unsigned int    cdrive;
  900.  
  901. /* If restricted shell - illegal */
  902.  
  903.     if (check_rsh ("cd"))
  904.     return 1;
  905.  
  906. /* Use default ? */
  907.  
  908.     if (((p = t->words[1]) == (char *)NULL) &&
  909.     ((p = lookup (home, FALSE)->value) == null))
  910.     {
  911.     print_error ("cd: no home directory\n");
  912.     return 1;
  913.     }
  914.  
  915. /* Save the current drive */
  916.  
  917.     _dos_getdrive (&cdrive);
  918.  
  919. /* Scan for the directory.  If there is not a / or : at start, use the
  920.  * CDPATH variable
  921.  */
  922.  
  923.     cp = (*p == '/') ? null : lookup ("CDPATH", FALSE)->value;
  924.     cp = (*(p + 1) == ':') ? null : cp;
  925.  
  926.     do
  927.     {
  928.     cp = path_append (cp, p, e.linep);
  929.  
  930. /* Check for new disk drive */
  931.  
  932.     nd = e.linep;
  933.  
  934.     if (*(nd+ 1) == ':')
  935.     {
  936.         _dos_setdrive (tolower (*nd) - 'a' + 1, &dummy);
  937.         nd += 2;
  938.     }
  939.  
  940. /* Was the change successful? */
  941.  
  942.     if ((!*nd) || (chdir (nd) == 0))
  943.     {
  944.  
  945. /* OK - reset the current directory (in the shell) and display the new
  946.  * path if appropriate
  947.  */
  948.  
  949.         Getcwd ();
  950.  
  951.         if (first || (strchr (p, '/') != (char *)NULL))
  952.         dopwd (t);
  953.  
  954.         return 0;
  955.     }
  956.  
  957.     first = 1;
  958.  
  959.     } while (cp != (char *)NULL);
  960.  
  961. /* Restore our original drive and restore directory info */
  962.  
  963.     _dos_setdrive (cdrive, &dummy);
  964.     Getcwd ();
  965.  
  966.     print_error ("%s: bad directory\n", p);
  967.     return 1;
  968. }
  969.  
  970. /*
  971.  * Extract the next path from a string and build a new path from the
  972.  * extracted path and a file name
  973.  */
  974. char        *path_append (s1, s2, si)
  975. register char    *s1;            /* Path string            */
  976. register char    *s2;            /* File name string        */
  977. char        *si;            /* Output path            */
  978. {
  979.     register char    *s;
  980.  
  981.     s = si;
  982.  
  983.     while (*s1 && *s1 != ';')
  984.     *s++ = *s1++;
  985.  
  986.     if ((si != s) && (*(s - 1) != '/'))
  987.     *s++ = '/';
  988.  
  989.     *s = '\0';
  990.  
  991.     if (s2 != (char *)NULL)
  992.     strcpy (s, s2);
  993.  
  994.     return (*s1 ? ++s1 : (char *)NULL);
  995. }
  996.  
  997. /*
  998.  * Execute a shift command: shift <n>
  999.  */
  1000.  
  1001. static int    doshift (t)
  1002. register C_Op    *t;
  1003. {
  1004.     register int    n;
  1005.  
  1006.     n = (t->words[1] != (char *)NULL) ? getn (t->words[1]) : 1;
  1007.  
  1008.     if (dolc < n)
  1009.     {
  1010.     print_error ("sh: nothing to shift\n");
  1011.     return 1;
  1012.     }
  1013.  
  1014.     dolv[n] = dolv[0];
  1015.     dolv += n;
  1016.     dolc -= n;
  1017.     setval (lookup ("#", TRUE), putn (dolc));
  1018.     return 0;
  1019. }
  1020.  
  1021. /*
  1022.  * Execute a umask command: umask <n>
  1023.  */
  1024.  
  1025. static int    doumask (t)
  1026. register C_Op    *t;
  1027. {
  1028.     register int    i;
  1029.     register char    *cp;
  1030.  
  1031.     if ((cp = t->words[1]) == (char *)NULL)
  1032.     {
  1033.     i = umask (0);
  1034.     umask (i);
  1035.     v1printf ("%o\n", i);
  1036.     }
  1037.  
  1038.     else
  1039.     {
  1040.     i = 0;
  1041.     while (IS_OCTAL (*cp))
  1042.         i = i * 8 + (*(cp++) - '0');
  1043.  
  1044.     umask (i);
  1045.     }
  1046.  
  1047.     return 0;
  1048. }
  1049.  
  1050. /*
  1051.  * Execute an exec command: exec <arguments>
  1052.  */
  1053.  
  1054. static int    doexec (t)
  1055. register C_Op    *t;
  1056. {
  1057.     register int    i;
  1058.     jmp_buf        ex;
  1059.     int            *ofail;
  1060.  
  1061.     t->ioact = (IO_Actions **)NULL;
  1062.  
  1063.     for (i = 0; (t->words[i] = t->words[i + 1]) != (char *)NULL; i++)
  1064.     ;
  1065.  
  1066.     if (i == 0)
  1067.     return 0;
  1068.  
  1069.     execflg = 1;
  1070.     ofail = failpt;
  1071.  
  1072. /* Set execute function recursive level to zero */
  1073.  
  1074.     Execute_stack_depth = 0;
  1075.  
  1076.     if (setjmp (failpt = ex) == 0)
  1077.     execute (t, NOPIPE, NOPIPE, FEXEC);
  1078.  
  1079.     failpt = ofail;
  1080.     execflg = 0;
  1081.     return 1;
  1082. }
  1083.  
  1084. /*
  1085.  * Execute a script in the current shell
  1086.  */
  1087.  
  1088. static int    dodot (t)
  1089. C_Op        *t;
  1090. {
  1091.     register int    i;
  1092.     register char    *sp;
  1093.     char        *cp;
  1094.  
  1095.     if ((cp = t->words[1]) == (char *)NULL)
  1096.     return 0;
  1097.  
  1098.     sp = any ('/', cp) ? null : path->value;
  1099.  
  1100.     do
  1101.     {
  1102.     sp = path_append (sp, cp, e.linep);
  1103.  
  1104.     if ((i = O_for_execute (e.linep)) >= 0)
  1105.     {
  1106.         exstat = 0;
  1107.         next (remap (i));
  1108.         return exstat;
  1109.     }
  1110.     } while (sp != (char *)NULL);
  1111.  
  1112.     print_error ("%s: not found\n", cp);
  1113.     return 1;
  1114. }
  1115.  
  1116. /*
  1117.  * Read from standard input into a variable list
  1118.  */
  1119.  
  1120. static int    doread (t)
  1121. C_Op        *t;
  1122. {
  1123.     register char    *cp, **wp;
  1124.     register int    nb;
  1125.  
  1126.     if (t->words[1] == (char *)NULL)
  1127.     {
  1128.     print_error ("Usage: read name ...\n");
  1129.     return 1;
  1130.     }
  1131.  
  1132.     for (wp = t->words + 1; *wp != (char *)NULL; wp++)
  1133.     {
  1134.     for (cp = e.linep; cp < e.eline - 1; cp++)
  1135.     {
  1136.         if (((nb = read (STDIN_FILENO, cp, 1)) != 1) || (*cp == NL) ||
  1137.         ((wp[1] != (char *)NULL) && any (*cp, ifs->value)))
  1138.  
  1139.         break;
  1140.     }
  1141.  
  1142.     *cp = 0;
  1143.  
  1144.     if (nb <= 0)
  1145.         break;
  1146.  
  1147.     setval (lookup (*wp, TRUE), e.linep);
  1148.     }
  1149.  
  1150.     return (nb <= 0);
  1151. }
  1152.  
  1153. /*
  1154.  * Evaluate an expression
  1155.  */
  1156.  
  1157. static int    doeval (t)
  1158. register C_Op    *t;
  1159. {
  1160.     return RUN (awordlist, t->words + 1, wdchar);
  1161. }
  1162.  
  1163. /*
  1164.  * Execute a trap
  1165.  */
  1166.  
  1167. static int    dotrap (t)
  1168. register C_Op    *t;
  1169. {
  1170.     register int    n, i;
  1171.     register int    resetsig;
  1172.     char        tval[10];
  1173.     char        *cp;
  1174.  
  1175.  
  1176.     if (t->words[1] == (char *)NULL)
  1177.     {
  1178.  
  1179. /* Display trap - look up each trap and print those we find */
  1180.  
  1181.     for (i = 0; i < NSIG; i++)
  1182.     {
  1183.         sprintf (tval, "~%d", i);
  1184.  
  1185.         if ((cp = lookup (tval, FALSE)->value) != null)
  1186.         {
  1187.         v1printf ("%u: ", i);
  1188.         v1a_puts (cp);
  1189.         }
  1190.     }
  1191.  
  1192.     return 0;
  1193.     }
  1194.  
  1195.     resetsig = isdigit (*t->words[1]);        /* Reset signal?    */
  1196.  
  1197.     for (i = resetsig ? 1 : 2; t->words[i] != (char *)NULL; ++i)
  1198.     {
  1199.  
  1200. /* Generate the variable name */
  1201.  
  1202.     sprintf (tval, "~%d", (n = getsig (t->words[i])));
  1203.  
  1204.     if (n == -1)
  1205.         return 1;
  1206.  
  1207.     unset (tval, TRUE);
  1208.  
  1209. /* Re-define signal processing */
  1210.  
  1211.     if (!resetsig)
  1212.     {
  1213.         if (*t->words[1] != '\0')
  1214.         {
  1215.         setval (lookup (tval, TRUE), t->words[1]);
  1216.         setsig (n, sig);
  1217.         }
  1218.  
  1219.         else
  1220.         setsig (n, SIG_IGN);
  1221.     }
  1222.  
  1223. /* Clear signal processing */
  1224.  
  1225.     else if (talking)
  1226.     {
  1227.         if (n == SIGINT)
  1228.         setsig (n, onintr);
  1229.  
  1230.         else
  1231. #ifdef SIGQUIT
  1232.         setsig (n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
  1233. #else
  1234.         setsig (n, SIG_DFL);
  1235. #endif
  1236.     }
  1237.  
  1238.     else
  1239.         setsig (n, SIG_DFL);
  1240.     }
  1241.  
  1242.     return 0;
  1243. }
  1244.  
  1245. /*
  1246.  * Get a signal number
  1247.  */
  1248.  
  1249. static int    getsig (s)
  1250. char        *s;
  1251. {
  1252.     register int    n;
  1253.  
  1254.     if (((n = getn (s)) < 0) || (n >= NSIG))
  1255.     {
  1256.     print_error ("trap: bad signal number\n");
  1257.     n = -1;
  1258.     }
  1259.  
  1260.     return n;
  1261. }
  1262.  
  1263. /*
  1264.  * Set up a signal function
  1265.  */
  1266.  
  1267. static void    setsig (n, f)
  1268. register int    n;
  1269. int        (*f)();
  1270. {
  1271.     if (n == 0)
  1272.     return;
  1273.  
  1274.     if ((signal (n, SIG_IGN) != SIG_IGN) || (ourtrap & (1L << n)))
  1275.     {
  1276.     ourtrap |= (1L << n);
  1277.     signal (n, f);
  1278.     }
  1279. }
  1280.  
  1281. /* Convert a string to a number */
  1282.  
  1283. int    getn (as)
  1284. char    *as;
  1285. {
  1286.     char    *s;
  1287.     int        n = (int)strtol (as, &s, 10);
  1288.  
  1289.     if (*s)
  1290.     print_error ("%s: bad number\n", as);
  1291.  
  1292.     return n;
  1293. }
  1294.  
  1295. /*
  1296.  * BREAK and CONTINUE processing
  1297.  */
  1298.  
  1299. static int    dobreak (t)
  1300. C_Op        *t;
  1301. {
  1302.     return brkcontin (t->words[1], BC_BREAK);
  1303. }
  1304.  
  1305. static int    docontinue (t)
  1306. C_Op        *t;
  1307. {
  1308.     return brkcontin (t->words[1], BC_CONTINUE);
  1309. }
  1310.  
  1311. static int    brkcontin (cp, val)
  1312. register char    *cp;
  1313. int        val;
  1314. {
  1315.     register Break_C    *Break_Loc;
  1316.     register int    nl;
  1317.  
  1318.     if ((nl = (cp == (char *)NULL) ? 1 : getn (cp)) <= 0)
  1319.     nl = 999;
  1320.  
  1321.     do
  1322.     {
  1323.     if ((Break_Loc = Break_List) == (Break_C *)NULL)
  1324.         break;
  1325.  
  1326.     Break_List = Break_Loc->nextlev;
  1327.  
  1328.     } while (--nl);
  1329.  
  1330.     if (nl)
  1331.     {
  1332.     print_error ("sh: bad break/continue level\n");
  1333.     return 1;
  1334.     }
  1335.  
  1336.     longjmp (Break_Loc->brkpt, val);
  1337.  
  1338. /* NOTREACHED */
  1339. }
  1340.  
  1341. /*
  1342.  * Exit function
  1343.  */
  1344.  
  1345. static int    doexit (t)
  1346. C_Op        *t;
  1347. {
  1348.     Break_C    *SShell_Loc = SShell_List;
  1349.  
  1350.     execflg = 0;
  1351.  
  1352. /* Set up error codes */
  1353.  
  1354.     if (t->words[1] != (char *)NULL)
  1355.     {
  1356.     exstat = getn (t->words[1]);
  1357.     setval (lookup ("?", TRUE), t->words[1]);
  1358.     }
  1359.  
  1360. /* Are we in a subshell.  Yes - do a longjmp instead of an exit */
  1361.  
  1362.     if (SShell_Loc != (Break_C *)NULL)
  1363.     {
  1364.     SShell_List = SShell_Loc->nextlev;
  1365.     longjmp (SShell_Loc->brkpt, 1);
  1366.     }
  1367.  
  1368.     leave ();
  1369.     return 1;
  1370. }
  1371.  
  1372. /*
  1373.  * Function return - set exit value and return via a long jmp
  1374.  */
  1375.  
  1376. static int    doreturn (t)
  1377. C_Op        *t;
  1378. {
  1379.     Break_C    *Return_Loc = Return_List;
  1380.  
  1381.     if  (t->words[1] != (char *)NULL)
  1382.     setval (lookup ("?", TRUE), t->words[1]);
  1383.  
  1384. /* If the return address is defined - return to it.  Otherwise, return
  1385.  * the value
  1386.  */
  1387.  
  1388.     if (Return_Loc != (Break_C *)NULL)
  1389.     {
  1390.     Return_List = Return_Loc->nextlev;
  1391.     longjmp (Return_Loc->brkpt, 1);
  1392.     }
  1393.  
  1394.     return getn (t->words[1]);
  1395. }
  1396.  
  1397. /*
  1398.  * MSDOS, EXPORT and READONLY functions
  1399.  */
  1400.  
  1401. static int    doexport (t)
  1402. C_Op        *t;
  1403. {
  1404.     return rdexp (t->words + 1, EXPORT, "export ");
  1405. }
  1406.  
  1407. static int    doreadonly (t)
  1408. C_Op         *t;
  1409. {
  1410.     return rdexp (t->words + 1, RONLY, "readonly ");
  1411. }
  1412.  
  1413. static int    domsdos (t)
  1414. C_Op        *t;
  1415. {
  1416.     return rdexp (t->words + 1, C_MSDOS, "msdos ");
  1417. }
  1418.  
  1419. static int    rdexp (wp, key, tstring)
  1420. register char    **wp;
  1421. int        key;
  1422. char        *tstring;
  1423. {
  1424.     char    *cp;
  1425.     bool    valid;
  1426.  
  1427.     if (*wp != (char *)NULL)
  1428.     {
  1429.     for (; *wp != (char *)NULL; wp++)
  1430.     {
  1431.         cp = *wp;
  1432.         valid = TRUE;
  1433.  
  1434. /* Check for a valid name */
  1435.  
  1436.         if (!isalpha (*(cp++)))
  1437.         valid = FALSE;
  1438.  
  1439.         else
  1440.         {
  1441.         while (*cp)
  1442.         {
  1443.             if (!isalnum (*(cp++)))
  1444.             {
  1445.             valid = FALSE;
  1446.             break;
  1447.             }
  1448.         }
  1449.         }
  1450.  
  1451. /* If valid - update, otherwise print a message */
  1452.  
  1453.         if (valid)
  1454.         s_vstatus (lookup (*wp, TRUE), key);
  1455.  
  1456.         else
  1457.         print_error ("%s: bad identifier\n", *wp);
  1458.     }
  1459.     }
  1460.  
  1461.     else
  1462.     {
  1463.     register Var_List    *vp;
  1464.  
  1465.     for (vp = vlist; vp != (Var_List *) NULL; vp = vp->next)
  1466.     {
  1467.         if ((vp->status & key) && isalpha (*vp->name))
  1468.         {
  1469.         v1_puts (tstring);
  1470.         v1_putsn (vp->name, (int)(findeq (vp->name) - vp->name));
  1471.         v1_putc (NL);
  1472.         }
  1473.     }
  1474.     }
  1475.  
  1476.     return 0;
  1477. }
  1478.  
  1479. /*
  1480.  * Sort Compare function for displaying variables
  1481.  */
  1482.  
  1483. int    sort_compare (s1, s2)
  1484. char    **s1;
  1485. char    **s2;
  1486. {
  1487.     return strcmp (*s1, *s2);
  1488. }
  1489.  
  1490. /*
  1491.  * Set function
  1492.  */
  1493.  
  1494. static int    doset (t)
  1495. register C_Op    *t;
  1496. {
  1497.     register Var_List    *vp;
  1498.     register char    *cp;
  1499.     register int    n, j;
  1500.     Fun_Ops        *fp;
  1501.     char        sign;
  1502.     char        **list;
  1503.  
  1504. /* Display ? */
  1505.  
  1506.     if ((cp = t->words[1]) == (char *)NULL)
  1507.     {
  1508.  
  1509. /* Count the number of entries to print */
  1510.  
  1511.     for (n = 0, vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
  1512.     {
  1513.         if (isalnum (*vp->name))
  1514.         n++;
  1515.     }
  1516.  
  1517. /* Build a local array of name */
  1518.  
  1519.     list = (char **)space (sizeof (char *) * n);
  1520.  
  1521.     for (n = 0, vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
  1522.     {
  1523.         if (isalnum (*vp->name))
  1524.         {
  1525.         if (list == (char **)NULL)
  1526.             v1a_puts (vp->name);
  1527.  
  1528.         else
  1529.             list[n++] = vp->name;
  1530.         }
  1531.     }
  1532.  
  1533. /* Sort them and then print */
  1534.  
  1535.     if (list != (char **)NULL)
  1536.     {
  1537.         qsort (list, n, sizeof (char *), sort_compare);
  1538.  
  1539.         for (j = 0; j < n; j++)
  1540.         v1a_puts (list[j]);
  1541.  
  1542.         DELETE (list);
  1543.     }
  1544.  
  1545. /* Print the list of functions */
  1546.  
  1547.     for (fp = fun_list; fp != (Fun_Ops *)NULL; fp = fp->next)
  1548.         Print_ExTree (fp->tree);
  1549.  
  1550.     return 0;
  1551.     }
  1552.  
  1553. /* Set/Unset a flag ? */
  1554.  
  1555.     if (((sign = *cp) == '-') || (*cp == '+'))
  1556.     {
  1557.     for (n = 0; (t->words[n] = t->words[n + 1]) != (char *)NULL; n++)
  1558.         ;
  1559.  
  1560.     for (; *cp; cp++)
  1561.     {
  1562.         if (*cp == 'r')
  1563.         {
  1564.         print_error ("set: -r bad option\n");
  1565.         return 1;
  1566.         }
  1567.  
  1568.         if (*cp == 'e')
  1569.         {
  1570.         if (!talking)
  1571.         {
  1572.             if (sign == '-')
  1573.             FL_SET ('e');
  1574.  
  1575.             else
  1576.             FL_CLEAR ('e');
  1577.         }
  1578.         }
  1579.  
  1580.         else if (islower (*cp))
  1581.         {
  1582.         if (sign == '-')
  1583.             FL_SET (*cp);
  1584.  
  1585.         else
  1586.             FL_CLEAR (*cp);
  1587.         }
  1588.     }
  1589.  
  1590.     setdash ();
  1591.     }
  1592.  
  1593. /* Set up parameters ? */
  1594.  
  1595.     if (t->words[1])
  1596.     {
  1597.     t->words[0] = dolv[0];
  1598.  
  1599.     for (n = 1; t->words[n] != (char *)NULL; n++)
  1600.         setarea ((char *)t->words[n], 0);
  1601.  
  1602.     dolc = n-1;
  1603.     dolv = t->words;
  1604.     setval (lookup ("#", TRUE), putn (dolc));
  1605.     setarea ((char *)(dolv - 1), 0);
  1606.     }
  1607.  
  1608.     return 0;
  1609. }
  1610.  
  1611. /*
  1612.  * History functions - display, initialise, enable, disable
  1613.  */
  1614.  
  1615. static int    dohistory (t)
  1616. C_Op        *t;
  1617. {
  1618.     char    *cp;
  1619.  
  1620.     if (!talking)
  1621.     return 1;
  1622.  
  1623.     if ((cp = t->words[1]) == (char *)NULL)
  1624.     Display_History ();
  1625.  
  1626.     else if (strcmp (cp, "-i") == 0)
  1627.     Clear_History ();
  1628.  
  1629.     else if (strcmp (cp, "-d") == 0)
  1630.     History_Enabled = FALSE;
  1631.  
  1632.     else if (strcmp (cp, "-e") == 0)
  1633.     History_Enabled = TRUE;
  1634.  
  1635.     return 0;
  1636. }
  1637.  
  1638. /*
  1639.  * Type fucntion: For each name, indicate how it would be interpreted
  1640.  */
  1641.  
  1642. static char    *type_ext[] = {
  1643.     "", ".exe", ".com", ".sh"
  1644. };
  1645.  
  1646. static int    dotype (t)
  1647. register C_Op    *t;
  1648. {
  1649.     register char    *sp;            /* Path pointers    */
  1650.     char        *cp;
  1651.     char        *ep;
  1652.     char        *xp;            /* In file name pointers */
  1653.     char        *xp1;
  1654.     int            n = 1;            /* Argument count    */
  1655.     int            i, fp;    
  1656.     bool        found;            /* Found flag        */
  1657.  
  1658.     while ((cp = t->words[n++]) != (char *)NULL)
  1659.     {
  1660.     sp = any ('/', cp) ? null : path->value;
  1661.     found = FALSE;
  1662.  
  1663.     do
  1664.     {
  1665.         sp = path_append (sp, cp, e.linep);
  1666.         ep = &e.linep[strlen (e.linep)];
  1667.  
  1668. /* Get start of file name */
  1669.  
  1670.         if ((xp1 = strrchr (e.linep, '/')) == (char *)NULL)
  1671.         xp1 = e.linep;
  1672.         
  1673.         else
  1674.         ++xp1;
  1675.  
  1676. /* Look up all 4 types */
  1677.  
  1678.         for (i = 0; (i < 4) && !found; i++)
  1679.         {
  1680.         strcpy (ep, type_ext[i]);
  1681.  
  1682.         if (access (e.linep, F_OK) == 0)
  1683.         {
  1684.  
  1685. /* If no extension or .sh extension, check for shell script */
  1686.  
  1687.             if (((xp = strchr (xp1, '.')) == (char *)NULL) ||
  1688.             (stricmp (xp, ".sh") == 0))
  1689.             {
  1690.             if ((fp = Check_Script (e.linep)) < 0)
  1691.                 continue;
  1692.  
  1693.             S_close (fp, TRUE);
  1694.             }
  1695.  
  1696.             else if ((stricmp (xp, ".exe") != 0) &&
  1697.                  (stricmp (xp, ".com") != 0))
  1698.             continue;
  1699.  
  1700.             print_error ("%s is %s\n", cp, e.linep);
  1701.             found = TRUE;
  1702.         }
  1703.         }
  1704.     } while ((sp != (char *)NULL) && !found);
  1705.  
  1706.     if (!found)
  1707.         print_error ("%s not found\n", cp);
  1708.     }
  1709.  
  1710.     return 0;
  1711. }
  1712.  
  1713. /* Table of internal commands */
  1714.  
  1715. static struct    builtin    builtin[] = {
  1716.     ".",        dodot,
  1717.     ":",        dolabel,
  1718.     "[",        dotest,
  1719.     "break",    dobreak,
  1720.     "cd",        dochdir,
  1721.     "continue",    docontinue,
  1722.     "echo",        doecho,
  1723.     "eval",        doeval,
  1724.     "exec",        doexec,
  1725.     "exit",        doexit,
  1726.     "export",    doexport,
  1727.     "getopt",    dogetopt,
  1728.     "history",    dohistory,
  1729.     "msdos",    domsdos,
  1730.     "pwd",        dopwd,
  1731.     "read",        doread,
  1732.     "readonly",    doreadonly,
  1733.     "return",    doreturn,
  1734.     "set",        doset,
  1735.     "shift",    doshift,
  1736.     "swap",        doswap,
  1737.     "test",        dotest,
  1738.     "trap",        dotrap,
  1739.     "type",        dotype,
  1740.     "umask",    doumask,
  1741.     "unset",    dounset,
  1742.     "ver",        dover,
  1743.     (char *)NULL,
  1744. };
  1745.  
  1746. /*
  1747.  * Look up a built in command
  1748.  */
  1749.  
  1750. int        (*inbuilt (s))()
  1751. register char    *s;
  1752. {
  1753.     register struct builtin    *bp;
  1754.  
  1755.     if ((strlen (s) == 2) && isalpha (*s) && (*s != '_') && (*(s + 1) == ':'))
  1756.     return dodrive;
  1757.  
  1758.     for (bp = builtin; bp->command != (char *)NULL; bp++)
  1759.     {
  1760.     if (stricmp (bp->command, s) == 0)
  1761.         return bp->fn;
  1762.     }
  1763.  
  1764.     return NULL;
  1765. }
  1766.  
  1767. /* Write to stdout functions - printf, fputs, fputc, and a special */
  1768.  
  1769. /*
  1770.  * Equivalent of printf without using streams
  1771.  */
  1772.  
  1773. void    v1printf (fmt)
  1774. char    *fmt;
  1775. {
  1776.     va_list    ap;
  1777.     char    x[100];
  1778.  
  1779.     va_start (ap, fmt);
  1780.     vsprintf (x, fmt, ap);
  1781.     v1_puts (x);
  1782.     va_end (ap);
  1783. }
  1784.  
  1785. /*
  1786.  * Write string to STDOUT
  1787.  */
  1788.  
  1789. void        v1_puts (s)
  1790. char        *s;
  1791. {
  1792.     write (STDOUT_FILENO, s, strlen (s));
  1793. }
  1794.  
  1795. /*
  1796.  * Write string to STDOUT with a NL at end
  1797.  */
  1798.  
  1799. void        v1a_puts (s)
  1800. char        *s;
  1801. {
  1802.     char    c = NL;
  1803.  
  1804.     write (STDOUT_FILENO, s, strlen (s));
  1805.     write (STDOUT_FILENO, &c, 1);
  1806. }
  1807.  
  1808. /*
  1809.  * Write n characters to STDOUT
  1810.  */
  1811.  
  1812. static void    v1_putsn (s, n)
  1813. char        *s;
  1814. int        n;
  1815. {
  1816.     write (STDOUT_FILENO, s, n);
  1817. }
  1818.  
  1819. /*
  1820.  * Write 1 character to STDOUT
  1821.  */
  1822.  
  1823. void        v1_putc (c)
  1824. char        c;
  1825. {
  1826.     write (STDOUT_FILENO, &c, 1);
  1827. }
  1828.